package cn.jinjimi.app.main.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewParent;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;

import java.util.List;


/**
 * Created by lforxeverc on 2017/1/3.
 */

public class ChartView extends View {
    Paint mOrangePointPaint, mWhitePointPaint;
    Paint mLinePaint, mShadowPaint;
    Paint mBaseLinePaint;
    Path mShadowPath;
    TextPaint mYearTextPaint, mMonthTextPaint, mSumTextPaint;
    Adapter mAdapter;
    int colorOrange = 0xffff6430;
    int colorGray = 0xff999999;
    float density = 0;
    LinearGradient mGradient;

    float yearPaddingBottom = 4f;
    float monthPaddingYear = 2f;
    float baselinePaddingMonth = 3f;
    float sumPaddingBottom = 3f;

    float paddingTop = 15f;

    float topTextSize = 11f;
    float monthTextSize = 11f;
    float yearTextSize = 8f;
    float lineWidth = 2f;
    float baselineWidth = 1f;

    float distance = 50f;

    float orangePointWidth = 5f;
    float whitePointWidth = 4f;
    int bitmapWidth = 1000;


    int height, width;
    double sumTextHeight;
    double monthTextHeight;
    double yearTextHeight;
    double yearBottom;
    double monthBottom;
    double baselineBottom;
    ValueAnimator animator;
    Bitmap[] mBms;
    float x = 0;
    long animDuration = 1000;

    public ChartView(Context context) {
        this(context, null);
    }

    public ChartView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setDrawingCacheEnabled(false);
        setWillNotDraw(false);
        density = getResources().getDisplayMetrics().density;
        yearPaddingBottom *= density;
        monthPaddingYear *= density;
        baselinePaddingMonth *= density;
        sumPaddingBottom *= density;

        paddingTop *= density;

        topTextSize *= density;
        monthTextSize *= density;
        yearTextSize *= density;
        lineWidth *= density;

        distance *= density;

        orangePointWidth *= density;
        whitePointWidth *= density;

        mOrangePointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mOrangePointPaint.setColor(colorOrange);
        mOrangePointPaint.setStyle(Paint.Style.FILL);
        mOrangePointPaint.setStrokeWidth(orangePointWidth);

        mWhitePointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mWhitePointPaint.setColor(Color.WHITE);
        mWhitePointPaint.setStrokeWidth(whitePointWidth);
        mWhitePointPaint.setStyle(Paint.Style.FILL);

        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setColor(colorOrange);
        mLinePaint.setStyle(Paint.Style.FILL);
        mLinePaint.setStrokeWidth(lineWidth);

        mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mShadowPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));

        mMonthTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mMonthTextPaint.setTextSize(monthTextSize);
        mMonthTextPaint.setColor(colorGray);

        mYearTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mYearTextPaint.setTextSize(yearTextSize);
        mYearTextPaint.setColor(colorGray);

        mSumTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mSumTextPaint.setTextSize(topTextSize);
        mSumTextPaint.setColor(colorOrange);

        mBaseLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBaseLinePaint.setColor(colorGray);
        mBaseLinePaint.setStrokeWidth(baselineWidth);

        animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(animDuration);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                x = (Float) animation.getAnimatedValue();
                if (x == 1) {
                    mBms = loadBitmapFromView();
                    setDrawingCacheEnabled(false);
                    setLayerType(LAYER_TYPE_HARDWARE, null);
                }
                invalidate();
            }
        });
    }

    public void onDestroy() {
        resetBms();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mAdapter == null) {
            ViewParent vp = getParent();
            if (vp != null && vp instanceof HorizontalScrollView) {
                int w = ((HorizontalScrollView) vp).getWidth();
                int h = ((HorizontalScrollView) vp).getHeight();
                if (w != 0 && h != 0)
                    setMeasuredDimension(w, h);
                else {
                    setMeasuredDimension(1, 1);
                }
            }
            return;
        }
        height = MeasureSpec.getSize(heightMeasureSpec);
        width = (int) (mAdapter.getCount() * distance);
        setMeasuredDimension(width, height);
        yearBottom = height - yearPaddingBottom;
        monthBottom = yearBottom - yearTextHeight - monthPaddingYear;
        baselineBottom = monthBottom - monthTextHeight - baselinePaddingMonth;
    }

    public void setAdapter(Adapter mAdapter) {
        if (mAdapter == null || mAdapter.equals(this.mAdapter)) {
            return;
        }
        this.mAdapter = mAdapter;
        setDrawingCacheEnabled(true);
        mShadowPath = new Path();//reset
        resetBms();
        x = 0;

        sumTextHeight = getTxtHeight(mSumTextPaint);
        monthTextHeight = getTxtHeight(mMonthTextPaint);
        yearTextHeight = getTxtHeight(mYearTextPaint);
        requestLayout();
        animator.start();
        final ViewParent vp = getParent();
        if (vp != null && vp instanceof HorizontalScrollView) {
            post(new Runnable() {
                @Override
                public void run() {
                    ((HorizontalScrollView) vp).fullScroll(ScrollView.FOCUS_RIGHT);
                }
            });

        }
    }

    private void resetBms() {
        if (mBms != null) {
            for (Bitmap b : mBms) {
                if (b != null)
                    b.recycle();
            }
            mBms = null;
        }
    }


    private Bitmap[] loadBitmapFromView() {
        Bitmap all = getDrawingCache();
        if (all == null) return null;
        int width = all.getWidth();
        int height = all.getHeight();
        int pieces = width % bitmapWidth == 0 ? width / bitmapWidth : (width / bitmapWidth) + 1;
        Bitmap[] screenshot = new Bitmap[pieces];
        for (int i = 0; i < pieces; i++) {
            Bitmap p;
            if (i == pieces - 1) {
                p = Bitmap.createBitmap(all, i * bitmapWidth, 0, width - i * bitmapWidth, height);
            } else {
                p = Bitmap.createBitmap(all, i * bitmapWidth, 0, bitmapWidth, height);
            }
            screenshot[i] = p;
        }
        return screenshot;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mAdapter == null || mAdapter.getCount() == 0)
            return;
        if (mBms != null && mBms.length > 0) {
            for (int i = 0; i < mBms.length; i++) {
                Bitmap b = mBms[i];
                if (b == null) return;
                canvas.drawBitmap(b, i * bitmapWidth, 0, null);
            }
            return;
        }
        canvas.drawColor(Color.WHITE);
        int id = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        List<Data> mData = mAdapter.getData();
        int top = mAdapter.getTopSum();
        double grade = 0;
        if (top != 0) {
            grade = (baselineBottom - paddingTop - sumTextHeight - sumPaddingBottom) / top;
        }
        if (mGradient == null) {
            mGradient = new LinearGradient(0, 0, 0, (float) baselineBottom, colorOrange, Color.WHITE, Shader.TileMode.CLAMP);
            mShadowPaint.setShader(mGradient);
        }
        float firstPadding = 0;
        canvas.drawLine(0, (float) baselineBottom, width, (float) baselineBottom, mBaseLinePaint);
        for (int i = 0; i < mData.size(); i++) {
            Data current = mData.get(i);
            Data next = null;
            boolean currentIsLast = i == mData.size() - 1;
            boolean nextIsLast = false;
            if (!currentIsLast) {
                next = mData.get(i + 1);
                nextIsLast = i == mData.size() - 2;
            }

            if (i == 0) {
                firstPadding = mMonthTextPaint.measureText(current.getMonth()) / 2;
                mShadowPath.moveTo(firstPadding, (float) baselineBottom);
            }
            float cx = firstPadding + i * distance;
            float cy = (float) ((baselineBottom - x * current.getSum() * grade));
            if (!currentIsLast) {
                mShadowPath.lineTo(cx, cy);
                if (!nextIsLast)
                    canvas.drawLine(cx, cy, cx + distance, (float) ((baselineBottom - x * next.getSum() * grade)), mLinePaint);
                else {
                    mShadowPath.lineTo(cx, (float) baselineBottom);
                }
                mOrangePointPaint.setColor(colorOrange);
                canvas.drawCircle(cx, cy, orangePointWidth, mOrangePointPaint);
                canvas.drawCircle(cx, cy, whitePointWidth, mWhitePointPaint);
            } else {

                canvas.drawText(current.getMonth(), cx + distance, (float) monthBottom, mMonthTextPaint);
                mOrangePointPaint.setColor(colorGray);
                canvas.drawCircle(cx, cy, orangePointWidth, mOrangePointPaint);
                canvas.drawCircle(cx, cy, whitePointWidth, mWhitePointPaint);

            }
            if (current.showYear()) {
                canvas.drawText(current.getYear(), i * distance, (float) yearBottom, mYearTextPaint);
            }
            canvas.drawText(current.getMonth(), i * distance, (float) monthBottom, mMonthTextPaint);
            if (!currentIsLast) {
                String sum = String.valueOf(current.getSum());
                float length = mSumTextPaint.measureText(sum);
                canvas.drawText(sum, i * distance + firstPadding - length / 2, (float) (baselineBottom - sumPaddingBottom - orangePointWidth - x * current.getSum() * grade), mSumTextPaint);
            }
        }
        mShadowPath.lineTo(firstPadding, (float) baselineBottom);

        canvas.drawPath(mShadowPath, mShadowPaint);
        canvas.restoreToCount(id);


    }

    public double getTxtHeight(Paint mPaint) {
        Paint.FontMetrics fm = mPaint.getFontMetrics();
        return Math.ceil(fm.descent - fm.ascent);
    }

    public static class Adapter {
        List<Data> mData;

        public Adapter(List<Data> mData) {
            this.mData = mData;
        }

        public List<Data> getData() {
            return mData;
        }

        public int getCount() {
            if (mData != null && !mData.isEmpty())
                return mData.size();
            return 0;
        }

        public int getTopSum() {
            if (mData == null || mData.isEmpty())
                return 0;
            int top = 0;
            for (Data d : mData) {
                top = d.getSum() > top ? d.getSum() : top;
            }
            return top;
        }

        @Override
        public boolean equals(Object o) {
            if (o != null && o instanceof Adapter) {
                List<Data> data = ((Adapter) o).getData();
                if (data != null && data.size() == mData.size() && !data.isEmpty()) {

                    for (int i = 0; i < data.size(); i++) {
                        if (!data.get(i).equals(mData.get(i))) {
                            return false;
                        }
                    }
                    return true;
                }
            }
            return false;
        }
    }

    public static class Data {
        int sum;
        String monthStr;
        boolean unReach = false;

        public Data(String monthStr, int sum) {
            this.monthStr = monthStr;
            this.sum = sum;
        }

        public String getMonthStr() {
            return monthStr;
        }

        public String getMonth() {
            if (!TextUtils.isEmpty(monthStr)) {
                String month = monthStr.substring(monthStr.length() - 2);
                if (month.startsWith("0")) {
                    month = month.substring(1) + "月";
                } else {
                    month += "月";
                }
                return month;
            }
            return null;
        }

        @Override
        public boolean equals(Object o) {
            if (o != null && o instanceof Data) {
                return ((Data) o).getMonthStr().equals(getMonthStr()) && ((Data) o).getSum() == getSum();
            }
            return false;
        }

        public String getYear() {
            if (!TextUtils.isEmpty(monthStr)) {
                return monthStr.substring(0, 4);
            }
            return null;
        }

        public boolean showYear() {
            if (!TextUtils.isEmpty(monthStr)) {
                String month = monthStr.substring(monthStr.length() - 2);
                return "01".equals(month);
            }
            return false;
        }

        public int getSum() {
            return sum;
        }

        public void setUnReach(boolean unReach) {
            this.unReach = unReach;
        }
    }

}